/** \file    fpu.h
  * \brief   Controls state & modes of math-coprocessor.
  * \date    2001
  * \author  Fedor Chelnokov
  * \version $Header: /home/cscom1r4/cvsroot/cs/include/math/fpu.h,v 1.1 2004/12/10 05:10:45 cscom1r4 Exp $
  */

#ifndef _fpu_h_
#define _fpu_h_

#ifdef WIN32 //!__GNUC__

// QF2I == quick float to int

// preferable functions
// use QF2I_setMode() once, then QF2I() in a cycle or somehow else

/// mode of transformation from float to int, available in FPU
enum TransformationMode {
	toNearest = 0<<2,
	downward = 1<<2,
	upward = 2<<2,
	truncation = 3<<2
};

/// sets appropriate FPU truncation mode
TransformationMode QF2I_setMode( TransformationMode tm );
/// transformation (based on preset FPU mode) */
int QF2I( float from );
/// constructor-sets mode, destructor - resets original FPU mode
class CFPUmode {
public:
	CFPUmode( TransformationMode newMode ) 
		{ m_saveMode=QF2I_setMode(newMode); }
	~CFPUmode() 
		{ QF2I_setMode(m_saveMode); }
private:
	TransformationMode m_saveMode;
};

enum FpuException {
	FPU_IM = 1 << 0,	///< invalid operation mask
	FPU_DM = 1 << 1,	///< denormalized mask
	FPU_ZM = 1 << 2,	///< divide-by-zero mask
	FPU_OM = 1 << 3,	///< overflow mask
	FPU_UM = 1 << 4,	///< underflow mask
	FPU_PM = 1 << 5		///< precision mask
};

/// sets FPU to hide (mask) given exceptions
int enableFpuExcp( int excpMask );
/// constructor-sets mode, destructor - resets enabled FPU exceptions
class CFPUexcp {
public:
	CFPUexcp( int newMode ) 
		{ m_saveMode=enableFpuExcp(newMode); }
	~CFPUexcp() 
		{ enableFpuExcp(m_saveMode); }
private:
	int m_saveMode;
};

/////////////
// inlines //
/////////////

inline TransformationMode QF2I_setMode( TransformationMode tm ) {
	short __cw;
	char tm1 = (char)tm;
	char oldMode;

	_asm fstcw [__cw];
	_asm mov ax,[__cw];
	_asm mov bh, ah;
	_asm and bh, 0x0c;
	_asm mov oldMode, bh;
	_asm and ah, 0xf3;
	_asm or ah, tm1;
	_asm mov [__cw],ax;
	_asm fldcw [__cw];

	return TransformationMode(oldMode);
}

inline int enableFpuExcp( int excpMask ) {
	short __cw;
	char tm1 = (char)excpMask;
	char oldMode;

	_asm fnclex;
	_asm fstcw [__cw];
	_asm mov ax,[__cw];
	_asm mov bh, al;
	_asm and bh, 0x3f;
	_asm mov oldMode, bh;
	_asm and al, 0xc0;
	_asm mov bh, tm1;
	_asm and bh, 0x3f;
	_asm or al, bh;
	_asm mov [__cw],ax;
	_asm fldcw [__cw];

	return oldMode;
}

#pragma warning( push )
#pragma warning( disable : 869 )
inline int QF2I( float from ) {
	_asm fld from;
	int int_to;
	_asm fistp DWORD PTR [int_to];
//    assert( int_to == int(from) );
	return int_to;
}
#pragma warning( pop )

#endif //__GNUC__


#endif //_fpu_h_
